﻿/*
 @author Eric AMBROSI
 @alias ricko || rickota || ricota
 @classDescription allow to tween object in Silverlight 2
 @version 2.1.2 stable version
*/
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Diagnostics;
using System.Collections.Generic; 

namespace Tweened
{
    /// <summary>
    /// Tweened library permit to animate easily UIElements from shape to most complexe User Controls.
    /// His main objective consists in automate Storyboards and DoubleAnimation creation.
    /// </summary>
    public partial class Tween
    {
        #region Les Champs et Proriétés

       
        /// <summary>
        ///     FreeReference has been made to contains the reference of your choice. This allows you more flexible design code.
        ///     This Field is received when listening the TweenCompleted event or the ReverseCompleted event.
        /// </summary>
        public object FreeReference { get; set; }

        /// <summary>
        /// 	Indicates the current state of animation Tween - ReadOnly
        /// </summary>
        /// <returns>
        ///     return the current TweenedState Enumeration value : Tweening, Reversing, Stopped, Paused, RPaused, Completed
        /// </returns>
        public TweenedState ActualState { get { return _ActualState; } }
        private TweenedState _ActualState ;
        
        /// <summary>
        ///     	Indicates the number of animation sequences (tweenObject) currently administered by the Tween - ReadOnly
        /// </summary>
        /// <returns>
        ///     int
        /// </returns>
        public int Count { get { return tweenObjectList.Count; } }
        
        /// <summary>
        ///     Lists all states Tween
        /// </summary>
        public enum TweenedState 
        { 
            /// <summary>
            /// 	It is the state of tween when the animation is being.
            /// </summary>
            Tweening,
 
            /// <summary>
            ///     It is the state of tween when the animation back is being.
            /// </summary>
            Reversing,

            /// <summary>
            ///     It is the state of tween when no animation is being or when animation back is completed.
            /// </summary>
            Stopped,

            /// <summary>
            ///     It is the state of tween when the animation is paused.
            /// </summary>
            Paused,

            /// <summary>
            ///     It is the state of tween when the animation back is paused.
            /// </summary>
            RPaused,

            /// <summary>
            ///     It is the state of tween when the animation has been completed.
            /// </summary>
            Completed 
        }
        
        /// <summary>
        ///     Allows you to automatically manage the autoReverse Tween.
        /// </summary>
        /// <remarks>
        ///     the autoReverse not playing an animation offsetting tween but plays a new animation opposite.
        /// </remarks>
        public bool AutoReverse { get; set; }

        /// <summary>
        ///     BeginTime can shift the start of animation based on a number of seconds
        ///     Example:
        ///     myTween.BeginTime = = TimeSpan.FromSeconds (2);
        ///     Can shift the start of the animation 2 seconds
        /// </summary>
        public TimeSpan? BeginTime { get { return StoryboardIn.BeginTime; } set { StoryboardIn.BeginTime = value; } }
        
        /// <summary>
        ///     Can manage the speed of Tween. The default is 1. 
        ///     La valeur 0.5 multipliera la dure de l'animation par 2
        /// </summary>
        public double TweenSpeedRatio { get { return StoryboardIn.SpeedRatio; } set { StoryboardIn.SpeedRatio = value; } }
        
        
        /// <summary>
        ///     Can manage the speed of Reverse Tween. The default is 1.
        /// </summary>
        public double ReverseSpeedRatio { get { return StoryboardOut.SpeedRatio; } set { StoryboardOut.SpeedRatio = value; } }

        private Storyboard StoryboardIn = new Storyboard();
        private Storyboard StoryboardOut = new Storyboard();
        private List<TweenDescription> tweenObjectList= new List<TweenDescription>();
        


        #endregion

        #region Constructor(s)


        /// <summary>
        /// Constructor initializes a new Tween. Each Tween can contains lot of sequences or TweenObjects via AddSequences method.
        ///     <exemple>
        ///         <code>
        /// Tween myTween = new Tween();
        /// //or with inference
        /// var myTween = new Tween();
        ///         </code>
        ///     </exemple>
        /// </summary>
        public Tween()
        {

            _ActualState = TweenedState.Stopped;
            StoryboardIn.Completed += new EventHandler(StoryboardIn_Completed);
            StoryboardOut.Completed += new EventHandler(StoryboardOut_Completed);
            var g = Guid.NewGuid();
            //Debug.WriteLine("////////////////"+g.ToString());
            Application.Current.Resources.Add(g.ToString() + "_out", this.StoryboardOut);
            Application.Current.Resources.Add(g.ToString() + "_in", this.StoryboardIn);
        }

        #endregion

        #region les événements

        /// <summary>
        ///     The delegation defines the parameters of receipt when listening events "TweenCompleted" or "ReverseCompleted." 
        ///     The first parameter is the Tween itself, the second parameter refers to the public Tween.FreeReference field.
        ///     FreeReference has been made to contains the reference of your choice. This allows you more flexible design code.
        /// </summary>
        /// <param name="sender">The tween that has been completed.</param>
        /// <param name="freeReference">Anything you want to put here. You just have to define Tween.FreeReference field.</param>
        /// <seealso cref="FreeReference"/>
        public delegate void StoryboardComplete(Tween sender, object freeReference);
        
        /// <summary>
        ///     Event triggered when the animation Tween is complete.
        ///     <exemple>
        ///         <code>
        /// void Page_Loaded(object sender, RoutedEventArgs e)
        /// {
        ///     myTween.TweenCompleted += new Tween.StoryboardComplete(myTween_TweenCompleted);
        ///     
        ///     //myTween.FreeReference is typed with object, you set what ever you want.
        ///     myTween.FreeReference = "Hello the animation is finished.";
        /// }
        /// 
        /// void myTween_TweenCompleted(Tween sender, object freeReference)
        /// {
        ///     Debug.WriteLine("that tween {0} is over with that freeParameter :: {1}.", sender, freeReference);
        /// }
        ///         </code>
        ///     </exemple>
        /// </summary>
        public event StoryboardComplete TweenCompleted;
        
        /// <summary>
        ///     Event triggered when the animation back Tween is complete. 	for use with the AutoReverse or method Reverse
        ///     <exemple>
        ///         <code>
        /// void Page_Loaded(object sender, RoutedEventArgs e)
        /// {
        ///     myTween.TweenCompleted += new Tween.StoryboardComplete(myTween_TweenCompleted);
        ///     
        ///     //myTween.FreeReference is typed with object, you set what ever you want.
        ///     myTween.FreeReference = "Hello the animation BACK is finished.";
        /// }
        /// 
        /// void myTween_TweenCompleted(Tween sender, object freeReference )
        /// {
        ///     Debug.WriteLine("that tween {0} is over with that freeParameter :: {1}.", sender, freeReference);
        /// }
        ///         </code>
        ///     </exemple>
        /// </summary>
        public event StoryboardComplete ReverseCompleted;

        void StoryboardOut_Completed(object sender, EventArgs e)
        {
            if (ReverseCompleted != null) ReverseCompleted(this, this.FreeReference);
            _ActualState = TweenedState.Stopped;
        }

        void StoryboardIn_Completed(object sender, EventArgs e)
        {
            if (AutoReverse) { this.StoryboardOut.Begin(); _ActualState = TweenedState.Reversing; }
            else { _ActualState = TweenedState.Completed; }
            if (TweenCompleted != null) TweenCompleted(this, this.FreeReference);
        } 
        #endregion

        #region Tweened public Methods
        /// <summary>
        /// 	This mehod add a new animation sequence at Tween.
        ///     <exemple>
        ///         <code>
        /// //Consider we want to add 3 Tween objects : to1, to2, to3
        /// myTween.AddSequences( to1 );
        /// myTween.AddSequences( to2 );
        /// myTween.AddSequences( to3 );
        /// //A more compact syntaxt would be :
        /// myTween.AddSequences( to1, to2, to3  );
        ///         </code>
        ///     </exemple>
        /// </summary>
        /// <param name="tos">TweenDescriptions objects : </param>
        /// <see cref="TweenDescription"/>
        /// <returns>returns the TweenObject Added.</returns>
        public TweenDescription[] AddSequences(params TweenDescription[] tos)
        {
            foreach (TweenDescription to in tos)
            {
                tweenObjectList.Add(to);
                TransfertAnimation(to);
                
            }
            return tos;
        }

        /// <summary>
        ///     This mehod Delete an animation sequence Tween
        ///     <exemple>
        ///         <code>
        /// //Consider we have 3 Tween objects : to1, to2, to3 and we want delete 2 of them
        /// myTween.RemoveSequence( to2 );
        /// myTween.RemoveSequence( to3 );
        /// //A more compact syntaxt would be :
        /// myTween.RemoveSequence(  to2, to3  );
        ///         </code>
        ///     </exemple>
        /// </summary>
        /// <param name="tos">TweenDescription objects</param>
        /// <see cref="TweenDescription"/>
        /// <returns>returns an array of TweenDescriptions Deleted.</returns>
        public TweenDescription[] RemoveSequences(params TweenDescription[] tos)
        {
            foreach (TweenDescription to in tos)
            {
                tweenObjectList.Remove(to);
            }
            return tos;
        }

        /// <summary>
        ///     This mehod Delete all animations sequences contained by the Tween.
        ///     <exemple>
        ///         <code>
        ///  myTween.Clear();   
        ///         </code>
        ///     </exemple>
        /// </summary>
        public void ClearSequences()
        {
            tweenObjectList.Clear();
        }

        /// <summary>
        ///     This mehod Plays the animation. ActualState becomes TweenedState.Tweening.
        ///     <exemple>
        ///         <code>
        ///  myTween.Start();   
        ///         </code>
        ///     </exemple>
        /// </summary>
        public void Start()
        {
            if (_ActualState == TweenedState.Reversing) this.StoryboardOut.Stop();
            this.StoryboardIn.Begin(); 
            _ActualState = TweenedState.Tweening;
        }

        /// <summary>
        ///  Plays the animation back. ActualState becomes TweenedState.Reversing.
        ///     <exemple>
        ///         <code>
        /// myTween.Reverse();    
        ///         </code>
        ///     </exemple>
        /// </summary>
        public void Reverse()
        {
            if (_ActualState == TweenedState.Tweening) this.StoryboardIn.Stop();
            this.StoryboardOut.Begin();
            _ActualState = TweenedState.Reversing;
        }

        /// <summary>
        ///  Stop animation and animation back if needed. ActualState becomes TweenedState.Stopped.
        ///     <exemple>
        ///         <code>
        /// myTween.Stop();      
        ///         </code>
        ///     </exemple>
        /// </summary>
        public void Stop()
        {
            _ActualState = TweenedState.Stopped;
            this.StoryboardIn.Stop(); this.StoryboardOut.Stop();
        }

        /// <summary>
        ///     Pause or Resume current animation. 
        ///     ActualState becomes TweenedState.Paused or TweenedState.RPaused or TweenedState.Reversing or TweenedState.Tweening.
        ///     <exemple>
        ///         <code>
        /// myTween.Pause();      
        ///         </code>
        ///     </exemple>
        /// </summary>
        public void Pause()
        {
            switch (_ActualState)
            {
                case TweenedState.Tweening:
                    this.StoryboardIn.Pause();
                    _ActualState = TweenedState.Paused;
                    break;
                case TweenedState.Reversing:
                    this.StoryboardOut.Pause();
                    _ActualState = TweenedState.RPaused;
                    break;
                case TweenedState.Paused:
                    this.StoryboardIn.Resume();
                    _ActualState = TweenedState.Tweening;
                    break;
                case TweenedState.RPaused:
                    this.StoryboardOut.Pause();
                    _ActualState = TweenedState.Reversing;
                    break;
            }
        }

        #endregion

        #region transfert des DoubleAnimation aux StoryBoards
        private void TransfertAnimation(TweenDescription pTo)
        {
            //pas besoin de nom
            switch (pTo.TweenProperty.TType)
            {
                case TweenedType.Color:
                    this.StoryboardIn.Children.Add(((TweenColorDescription)pTo).aIn);
                    this.StoryboardOut.Children.Add(((TweenColorDescription)pTo).aOut);;
                    break;

                default:

                    this.StoryboardIn.Children.Add(((TweenDoubleDescription)pTo).aIn);
                    this.StoryboardOut.Children.Add(((TweenDoubleDescription)pTo).aOut);
                    break;
            }
        }
        #endregion

        
    }
}
